import { ref } from 'vue'
import { ceil, floor, random, shuffle } from 'lodash-es'
// 配置游戏的卡牌数  层数 关卡
const defaultGameConfig: GameConfig = {
  cardNum: 4,
  layerNum: 2,
  trap: true,
  delNode: false,
}

export function useGame(config: GameConfig): Game {
  const { container, delNode, events = {}, ...initConfig } = { ...defaultGameConfig, ...config }
  const histroyList = ref<CardNode[]>([]) // 历史记录
  const backFlag = ref(false) // 因为功能只能使用一次,做了flag限制
  const removeFlag = ref(false) // 同上
  const removeList = ref<CardNode[]>([]) // 存放移出的卡牌节点
  const preNode = ref<CardNode | null>(null) // 存放回退节点
  const nodes = ref<CardNode[]>([])
  const indexSet = new Set()
  let perFloorNodes: CardNode[] = []
  const selectedNodes = ref<CardNode[]>([])
  const size = 40
  let floorList: number[][] = []

  function updateState() {
    nodes.value.forEach((o) => {
      o.state = o.parents.every(p => p.state > 0) ? 1 : 0
    })
  }

  /**
   * 卡牌点击事件
   */
  function handleSelect(node: CardNode) {
    // 卡牌槽最多只有7个
    if (selectedNodes.value.length === 7)
      return
    node.state = 2
    histroyList.value.push(node)
    preNode.value = node
    const index = nodes.value.findIndex(o => o.id === node.id)
    if (index > -1)
      // 由于是默认删除节点，就无法处理动画，所以加了delNode的一个flag来处理。
      delNode && nodes.value.splice(index, 1)

    // 判断是否有可以消除的节点
    const selectedSomeNode = selectedNodes.value.filter(s => s.type === node.type)
    if (selectedSomeNode.length === 2) {
      // 第二个节点索引
      const secondIndex = selectedNodes.value.findIndex(o => o.id === selectedSomeNode[1].id)
      selectedNodes.value.splice(secondIndex + 1, 0, node)
      // 为了动画效果添加延迟
      setTimeout(() => {
        for (let i = 0; i < 3; i++) {
          // const index = selectedNodes.value.findIndex(o => o.type === node.type)
          selectedNodes.value.splice(secondIndex - 1, 1)
        }
        preNode.value = null
        // 判断是否已经清空节点，即是否胜利
        if (delNode ? nodes.value.length === 0 : nodes.value.every(o => o.state > 0) && removeList.value.length === 0 && selectedNodes.value.length === 0) {
          removeFlag.value = true
          backFlag.value = true
          events.winCallback && events.winCallback()
        }
        else {
          events.dropCallback && events.dropCallback()
        }
      }, 100)
    }
    else {
      events.clickCallback && events.clickCallback()
      const index = selectedNodes.value.findIndex(o => o.type === node.type)
      if (index > -1)
        selectedNodes.value.splice(index + 1, 0, node)
      else
        selectedNodes.value.push(node)
      // 判断卡槽是否已满，即失败
      if (selectedNodes.value.length === 7) {
        removeFlag.value = true
        backFlag.value = true
        events.loseCallback && events.loseCallback()
      }
    }
  }

  function handleSelectRemove(node: CardNode) {
    const index = removeList.value.findIndex(o => o.id === node.id)
    if (index > -1)
      removeList.value.splice(index, 1)
    handleSelect(node)
  }

  /*
   * 回退功能
  * */
  function handleBack() {
    const node = preNode.value
    // 当node存在时回退功能才能触发，因为在触发消除或者其他道具功能的时候，是无法触发回退的
    if (!node)
      return
    preNode.value = null
    backFlag.value = true
    node.state = 0
    delNode && nodes.value.push(node)
    const index = selectedNodes.value.findIndex(o => o.id === node.id)
    selectedNodes.value.splice(index, 1)
  }

  /*
   * 移出功能
  * */
  function handleRemove() {
    // 从selectedNodes.value中取出3个 到 removeList.value中

    if (selectedNodes.value.length < 3)
      return
    removeFlag.value = true
    preNode.value = null
    for (let i = 0; i < 3; i++) {
      const node = selectedNodes.value.shift()
      if (!node)
        return
      removeList.value.push(node)
    }
  }

  /*
 * 初始化数据
 * 1
 * @param {(GameConfig | null)} [config]
* */
  function initData(config?: GameConfig | null) {
    const { cardNum, layerNum, trap } = { ...initConfig, ...config }
    histroyList.value = []
    backFlag.value = false
    removeFlag.value = false
    removeList.value = []
    preNode.value = null
    nodes.value = []
    indexSet.clear()
    perFloorNodes = []
    selectedNodes.value = []
    floorList = []
    const isTrap = trap && floor(random(0, 100)) !== 50

    // 生成节点池
    const itemTypes = (new Array(cardNum).fill(0)).map((_, index) => index + 1)
    window.console.log('元素类型', itemTypes, '元素数量', cardNum)

    let itemList: number[] = []

    window.console.log('层数', layerNum)

    for (let i = 0; i < 3 * layerNum; i++) {
      // 扩展操作符（spread operator），将两个数组合并成一个新的数组并赋值给itemList变量
      itemList = [...itemList, ...itemTypes]
      window.console.log('节点列表遍历', itemTypes, '列表', itemList, '长度', itemList.length)
    }

    if (isTrap) {
      const len = itemList.length
      itemList.splice(len - cardNum, len)
      window.console.log('元素列表被删除数据', len - cardNum, len)
    }
    // 打乱节点 shuffle函数是lodash-es库中的一个函数，用于随机打乱一个数组中的元素顺序 主要使用了Fisher-Yates shuffle
    itemList = shuffle(shuffle(itemList))
    window.console.log('打乱后节点', itemList)

    // 初始化各个层级节点
    let len = 0
    let floorIndex = 1
    const itemLength = itemList.length
    window.console.log('卡牌数量', itemLength)

    while (len <= itemLength) {
      const maxFloorNum = floorIndex * floorIndex
      // window.console.log('最大层数', maxFloorNum)

      const floorNum = ceil(random(maxFloorNum / 2, maxFloorNum))
      window.console.log('层数', floorNum)

      floorList.push(itemList.splice(0, floorNum))
      window.console.log('层数列表', floorList)

      len += floorNum
      floorIndex++
    }

    // !是TypeScript的非空断言操作符 表示我们已经确定container.value不为null或undefined，所以可以直接使用container.value.clientWidth获取容器的宽度
    const containerWidth = container.value!.clientWidth
    const containerHeight = container.value!.clientHeight
    const width = containerWidth / 2
    const height = containerHeight / 2 - 60

    // 建立遮罩关系
    floorList.forEach((o, index) => {
      window.console.log('forEach参数', o, index)
      indexSet.clear()

      let i = 0
      const floorNodes: CardNode[] = []

      o.forEach((k) => {
        i = floor(random(0, (index + 1) ** 2))

        while (indexSet.has(i))
          i = floor(random(0, (index + 1) ** 2)) // ** 2 ((index + 1))的二次幂
        const row = floor(i / (index + 1))
        const column = index ? i % index : 0
        const node: CardNode = {
          id: `${index}-${i}`,
          type: k,
          zIndex:
            index,
          index: i,
          row,
          column,
          top: height + (size * row - (size / 2) * index),
          left: width + (size * column - (size / 2) * index),
          parents: [],
          state: 0,
        }
        const xy = [node.top, node.left]
        perFloorNodes.forEach((e) => {
          if (Math.abs(e.top - xy[0]) <= size && Math.abs(e.left - xy[1]) <= size)
            e.parents.push(node)
        })
        floorNodes.push(node)
        indexSet.add(i)
      })
      window.console.log('node:', nodes.value, 'floorNodes:', floorNodes)
      nodes.value = nodes.value.concat(floorNodes)
      perFloorNodes = floorNodes
    })

    updateState()
  }

  return {
    nodes,
    selectedNodes,
    removeFlag,
    removeList,
    backFlag,
    handleSelect,
    handleBack,
    handleRemove,
    handleSelectRemove,
    initData,
  }
}
